package com.windystory.wsmedialibrary.camera;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.view.Surface;
import android.view.TextureView;
import java.util.List;


/**
 * @description:
 * @author: fanrunqi
 * @date: 2019/4/17 12:00
 */
public class CameraManager implements Camera.PreviewCallback {

    private int degrees;
    private Camera mCamera;
    private int mCameraID;
    private ViewSize viewSize;
    private Camera.Size previewSize;
    private Activity activity;
    private Rect identifyRect;
    private TextureView textureView;
    private boolean isPause;
    private ToneGenerator mToneGenerator;
    public static final int LEFT = 0;
    public static final int TOP = 1;
    public static final int RIGHT = 2;
    public static final int BOTTOM = 3;
    private OnCameraActionCallback mOnCameraActionCallback;
    private boolean isPreviewing = false;
    private int mCurrentCameraFacing;//当前摄像头
    private int mDeviceOrientation = TOP;
    private static final int FRONT = 1;//前置摄像头标记
    private static final int BACK = 0;//后置摄像头标记
    private int currentCameraType = BACK;//当前打开的摄像头标记

    public CameraManager(Activity activity,
                         ViewSize viewSize,
                         TextureView textureView) {
        this.activity = activity;
        this.viewSize = viewSize;
        this.textureView = textureView;
        openCamera();
    }


    /**
     * 打开摄像头指定不摄像头
     *
     * @return
     */
    public CameraManager openCamera() {
        Camera.CameraInfo info = new Camera.CameraInfo();
        int numCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numCameras; i++) {
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                mCamera = Camera.open(i);
                mCameraID = info.facing;
                mCurrentCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
                break;
            }
        }
        if (mCamera == null) {
            mCamera = Camera.open();
            mCurrentCameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT;
            mCameraID = Camera.CameraInfo.CAMERA_FACING_FRONT;
        }
        if (mCamera == null) {
            throw new RuntimeException("Unable to open camera");
        }
        mCamera.setDisplayOrientation(calculateCameraPreviewOrientation());
        setPreviewSize();
        return this;
    }


    public void startPreview() {
        Matrix transform = calculateTextureTransform();
        textureView.setTransform(transform);
        if (mCamera != null) {
            try {
                mCamera.setPreviewTexture(textureView.getSurfaceTexture());
                mCamera.startPreview();
                mCamera.setOneShotPreviewCallback(this);
                isPreviewing = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    protected Matrix calculateTextureTransform() {

        float videoWidth, videoHeight;
        if (degrees == 0 || degrees == 180) {
            videoWidth = previewSize.height;
            videoHeight = previewSize.width;
        } else {
            videoWidth = previewSize.width;
            videoHeight = previewSize.height;
        }

        float ratioTexture = viewSize.width / (float) viewSize.height;
        float ratioPreview = videoWidth / videoHeight;
        float scaleX;
        float scaleY;

        if (ratioTexture < ratioPreview) {
            scaleX = ratioPreview / ratioTexture;
            scaleY = 1;
        } else {
            scaleX = 1;
            scaleY = ratioTexture / ratioPreview;
        }

        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY);
        float scaledWidth = viewSize.width * scaleX;
        float scaledHeight = viewSize.height * scaleY;
        float dx = (viewSize.width - scaledWidth) / 2;
        float dy = (viewSize.height - scaledHeight) / 2;
        matrix.postTranslate(dx, dy);

        return matrix;
    }


    public void setPreviewSize() {
        try {
            Camera.Parameters parameters = mCamera.getParameters();
            previewSize = calculatePerfectSize(parameters);
            parameters.setPreviewSize(previewSize.width, previewSize.height);
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            mCamera.setParameters(parameters);
        } catch (Exception e) {
        }
    }

    private Camera.Size calculatePerfectSize(Camera.Parameters parameters) {
        List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
        Camera.Size result = sizes.get(0);
        boolean isMatchHeight = true;
        if (viewSize.width > viewSize.height) {
            isMatchHeight = false;
        }

        for (Camera.Size size : sizes) {
            if (isMatchHeight) {
                if (Math.abs(result.height - viewSize.height)
                        > Math.abs(size.height - viewSize.height)) {
                    result = size;
                }
            } else {
                if (Math.abs(result.width - viewSize.width)
                        > Math.abs(size.width - viewSize.width)) {
                    result = size;
                }
            }
        }
        return result;
    }

    public int calculateCameraPreviewOrientation() {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraID, info);
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            result = (info.orientation - degrees + 360) % 360;
        } else {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        }
        return result;
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        if (mCamera == null || data.length < 1) {
            return;
        }

        int format = 0;
        try {
            format = camera.getParameters().getPreviewFormat();
        } catch (Exception e) {
            shotNextFrame();
        }

    }

    private void shotNextFrame() {
        if (mCamera != null) {
            mCamera.addCallbackBuffer(null);
            mCamera.setOneShotPreviewCallback(CameraManager.this);
        }
    }

    public void onResume() {
        isPause = false;
        if (mCamera == null) {
            openCamera();
        }
        startPreview();
    }

    public void onPause() {
        isPause = true;
        if (mCamera != null) {
            mCamera.stopPreview();
            isPreviewing = false;
        }
    }

    public void releaseCamera() {
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
            activity = null;
            textureView = null;
            isPreviewing = false;
        }
    }

    public void takePicture(OnCameraActionCallback callback) {
        mOnCameraActionCallback = callback;
        if (isPreviewing && mCamera != null) {
            mCamera.takePicture(mShutterCallback, null, mJpegPictureCallback);
        }
    }

    public interface OnCameraActionCallback {
        void onTakePictureComplete(Bitmap bitmap);
    }

    //对jpeg图像数据的回调,最重要的一个回调
    private Camera.PictureCallback mJpegPictureCallback = (data, camera) -> {
        //mCameraData = data;
        if (mOnCameraActionCallback != null) {
            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);//data是字节数据，将其解析成位图
            if (mCurrentCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                if (mDeviceOrientation == TOP) {
                    bitmap = rotateBitmap(bitmap, 90.0f);
                } else if (mDeviceOrientation == RIGHT) {
                    bitmap = rotateBitmap(bitmap, 180.0f);
                } else if (mDeviceOrientation == BOTTOM) {
                    bitmap = rotateBitmap(bitmap, 270.0f);
                }
            } else if (mCurrentCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                if (mDeviceOrientation == TOP) {
                    bitmap = rotateBitmap(bitmap, 270.0f);
                } else if (mDeviceOrientation == RIGHT) {
                    bitmap = rotateBitmap(bitmap, 180.0f);
                } else if (mDeviceOrientation == BOTTOM) {
                    bitmap = rotateBitmap(bitmap, 90.0f);
                }
            }
            mOnCameraActionCallback.onTakePictureComplete(bitmap);
        }
    };

    /**
     * 旋转图片
     *
     * @param bitmap 要旋转的图片
     * @param angle  旋转角度
     * @return bitmap
     */
    public static Bitmap rotateBitmap(Bitmap bitmap, float angle) {
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), matrix, true);
    }


    //快门按下的回调，在这里我们可以设置类似播放“咔嚓”声之类的操作。默认的就是咔嚓。
    private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {

        public void onShutter() {
            // TODO Auto-generated method stub
            if (mToneGenerator == null) {
                //发出提示用户的声音
                mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC,
                        ToneGenerator.MAX_VOLUME);
            }
            mToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2);
        }
    };


    /**
     * 切换摄像头
     */
    public void changeCamera() {
        if (mCamera != null) {
            mCamera.release();
            mCamera = null;
        }
        OpenChangeCamera();
        startPreview();
    }

    /**
     * 切换摄像头操作
     */
    private void OpenChangeCamera() {
        Camera.CameraInfo info = new Camera.CameraInfo();
        int numCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numCameras; i++) {
            Camera.getCameraInfo(i, info);
            if (currentCameraType == BACK) {
                if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    mCamera = Camera.open(i);//打开当前选中的摄像头
                    currentCameraType = FRONT;
                    mCameraID = info.facing;
                    mCurrentCameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT;
                    mCamera.setDisplayOrientation(calculateCameraPreviewOrientation());
                    setPreviewSize();
                    break;
                }
            } else {
                if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    mCamera = Camera.open(i);//打开当前选中的摄像头
                    currentCameraType = BACK;
                    mCameraID = info.facing;
                    mCurrentCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
                    mCamera.setDisplayOrientation(calculateCameraPreviewOrientation());
                    setPreviewSize();
                    break;
                }
            }

        }
    }

    /**
     * 通过设置Camera打开闪光灯
     */
    public synchronized void turnLightOn() {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        mCamera.setParameters(parameters);
    }

    /**
     * 通过设置Camera关闭闪光灯
     */
    public synchronized void turnLightOff() {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
        mCamera.setParameters(parameters);
    }
}